package com.g.ocache.impl;

import java.math.BigDecimal;
import java.text.NumberFormat;
import java.util.concurrent.atomic.AtomicLong;

import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;

import com.g.ocache.CacheStatistics;
import com.g.ocache.utils.EMA;


/**
 * 
 * @author Jingqi Xu
 */
public final class BasicCacheStatistics<K, V> implements CacheStatistics<K, V> {
	//
	private static final int DEFAULT_SAMPLING_PERIOD = 12;
	
	//
	private final AtomicLong hitCount;
	private final AtomicLong readCount;
	private final AtomicLong writeCount;
	private final NumberFormat percentFormat;
	
	//
	private final EMA<Long> loadTime;
	private final AtomicLong loadCount;
	private final AtomicLong vainlyLoadCount;
	private final AtomicLong unsuccessfullyLoadCount;
	
	/**
	 * 
	 */
	public BasicCacheStatistics() {
		//
		this.hitCount = new AtomicLong(0);
		this.readCount = new AtomicLong(0);
		this.writeCount = new AtomicLong(0);
		this.percentFormat = NumberFormat.getPercentInstance();
		this.percentFormat.setMaximumFractionDigits(1);
		
		//
		this.loadCount = new AtomicLong(0);
		this.vainlyLoadCount = new AtomicLong(0);
		this.unsuccessfullyLoadCount = new AtomicLong(0);
		this.loadTime = new EMA<Long>(DEFAULT_SAMPLING_PERIOD);
	}
	
	/**
	 * 
	 */
	public String toString() {
		return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
		.append("hitCount", getHitCount())
		.append("hitRatio", getHitRatioAsString())
		.append("readCount", getReadCount())
		.append("writeCount", getWriteCount())
		.append("loadCount", getLoadCount())
		.append("vainlyLoadCount", getVainlyLoadCount())
		.append("unsuccessfullyLoadCount", getUnsuccessfullyLoadCount())
		.append("avgLoadTime", getAverageLoadTime())
		.toString();
	}
	
	/**
	 * 
	 */
	public void reset() {
		hitCount.set(0);
		readCount.set(0);
		writeCount.set(0);
		loadCount.set(0);
		vainlyLoadCount.set(0);
		unsuccessfullyLoadCount.set(0);
		loadTime.reset();
	}
	
	public long getHitCount() {
		return hitCount.get();
	}
	
	public long addAndGetHitCount(long delta) {
		return hitCount.addAndGet(delta);
	}

	public BigDecimal getHitRatio() {
		//
		if(readCount.get() == 0) {
			return new BigDecimal("0");
		}
		if(hitCount.get() == 0) {
			return  new BigDecimal("0");
		}
		
		//
		BigDecimal r = new BigDecimal(hitCount.get());
		return r.divide(new BigDecimal(readCount.get()), 2, BigDecimal.ROUND_HALF_UP);
	}
	
	public String getHitRatioAsString() {
		return percentFormat.format(getHitRatio().doubleValue());
	}

	public long getReadCount() {
		return readCount.get();
	}
	
	public long addAndGetReadCount(long delta) {
		return readCount.addAndGet(delta);
	}

	public long getWriteCount() {
		return writeCount.get();
	}
	
	public long addAndGetWriteCount(long delta) {
		return writeCount.addAndGet(delta);
	}
	
	/**
	 * 
	 */
	public long getLoadCount() {
		return loadCount.get();
	}
	
	public long addAndGetLoadCount(long delta) {
		return loadCount.addAndGet(delta);
	}

	public long getVainlyLoadCount() {
		return vainlyLoadCount.get();
	}
	
	public long addAndGetVainlyLoadCount(long delta) {
		return vainlyLoadCount.addAndGet(delta);
	}
	
	public long getUnsuccessfullyLoadCount() {
		return unsuccessfullyLoadCount.get();
	}
	
	public long addAndGetUnsuccessfullyLoadCount(long delta) {
		return unsuccessfullyLoadCount.addAndGet(delta);
	}
	
	public double getAverageLoadTime() {
		return loadTime.getAverage();
	}
	
	public void sampleAverageLoadTime(long sample) {
		this.loadTime.sample(sample);
	}
}
